/*
 * Copyright (c) 2025 Industrial Software Feature Database
 */
package com.comac.ins.common.biz.event;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;

import com.comac.ins.common.biz.annotations.BizEventListener;
import com.comac.ins.common.biz.domain.db.BizEventInfo;
import com.comac.ins.common.biz.enums.BizEventStatusEnum;
import com.comac.ins.common.biz.exception.BizException;
import com.comac.ins.common.core.constant.Constants;
import com.comac.ins.common.json.utils.JsonUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import cn.hutool.extra.spring.SpringUtil;
import com.comac.ins.common.biz.domain.model.BizEvent;
import com.comac.ins.common.biz.enums.BizEventTypeEnum;
import lombok.extern.slf4j.Slf4j;

/**
 * 业务事件监听拦截切面
 *
 * @author zhanghongyi
 * @date 2024/7/5 15:32
 * @description BizEventListenerAspect
 */
@Aspect
@Component
@Slf4j
public class BizEventListenerAspect {

    @Autowired
    private BizEventService bizEventService;


    /**
     * 对加了注解的方法进行拦截
     */
    @Pointcut("@annotation(com.comac.ins.common.biz.annotations.BizEventListener)")
    public void pointCut() {
    }

    /**
     * 业务事件监听切面
     */
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        final String originalTraceId = MDC.get(Constants.TRACE_ID);
        try {
            BizEvent<?> event = extractBizEvent(joinPoint.getArgs());
            if (event != null) {
                initializeEventDetails(event);
                updateTraceId(event.getTraceId());
                return processAnnotation(joinPoint, event);
            }
            return joinPoint.proceed();
        } finally {
            restoreTraceId(originalTraceId);
        }
    }

    /**
     * 异步调用拦截的方法
     */
    @Async("bizEventListenerAsyncExecutor")
    public void callListenFunctionAsync(ProceedingJoinPoint proceedingJoinPoint, BizEvent<?> event) throws Throwable {
        BizEventInfo eventInfo = event.getEventInfo();
        try {
            BizEventUtil.setEvent(event);
            Object result = proceedingJoinPoint.proceed();
            eventInfo.setStatus(BizEventStatusEnum.DONE.getCode());
            eventInfo.setReturnResult(JsonUtils.toJsonString(result));
            eventInfo.setEndProcessTime(new Date());
            bizEventService.updateEventInfo(eventInfo);
        } catch (BizException e) {
            log.error("doEventProcess error", e);
            eventInfo.setStatus(BizEventStatusEnum.FAIL.getCode());
            eventInfo.setEndProcessTime(new Date());
            BizEventUtil.setCurrentMsg(eventInfo, e.getMessage());
            bizEventService.updateEventInfo(eventInfo);
        } catch (Throwable e) {
            log.error("doEventProcess error", e);
            eventInfo.setStatus(BizEventStatusEnum.FAIL.getCode());
            eventInfo.setEndProcessTime(new Date());
            BizEventUtil.setCurrentMsg(eventInfo, e.getMessage() + "#" + e.getCause());
            bizEventService.updateEventInfo(eventInfo);
        } finally {
            BizEventUtil.removeEvent();
        }
    }

    /**
     * 同步调用拦截的方法
     */
    public Object callListenFunction(ProceedingJoinPoint proceedingJoinPoint, BizEvent<?> event) {
        BizEventInfo eventInfo = event.getEventInfo();
        Object result = null;
        try {
            BizEventUtil.setEvent(event);
            result = proceedingJoinPoint.proceed();
            eventInfo.setStatus(BizEventStatusEnum.DONE.getCode());
            eventInfo.setReturnResult(JsonUtils.toJsonString(result));
            eventInfo.setEndProcessTime(new Date());
            bizEventService.updateEventInfo(eventInfo);
        } catch (Throwable e) {
            log.error("doEventProcess error", e);
            eventInfo.setStatus(BizEventStatusEnum.FAIL.getCode());
            eventInfo.setEndProcessTime(new Date());
            BizEventUtil.setCurrentMsg(eventInfo, e.getMessage() + "#" + e.getCause());
            bizEventService.updateEventInfo(eventInfo);
        } finally {
            BizEventUtil.removeEvent();
        }
        return result;
    }
    /**
     * 还原traceId
     */
    private void restoreTraceId(String originalTraceId) {
        if (StringUtils.isBlank(originalTraceId)) {
            MDC.remove(Constants.TRACE_ID);
        } else {
            MDC.put(Constants.TRACE_ID, originalTraceId);
        }
    }

    private BizEvent<?> extractBizEvent(Object[] params) {
        return Arrays.stream(params)
            .filter(p -> p instanceof BizEvent)
            .findFirst()
            .map(BizEvent.class::cast)
            .orElse(null);
    }

    /**
     * 初始化事件详情
     */
    private void initializeEventDetails(BizEvent<?> event) {
        if (event.getEventInfo() == null) {
            event.setEventInfo(bizEventService.initEventInfo(event));
        }
    }

    /**
     * 更新traceId
     */
    private void updateTraceId(String traceId) {
        if (StringUtils.isNotBlank(traceId)) {
            MDC.put(Constants.TRACE_ID, traceId);
        }
    }

    /**
     * 处理注解
     */
    private Object processAnnotation(ProceedingJoinPoint joinPoint, BizEvent<?> event) throws Throwable {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        BizEventListener annotation = method.getAnnotation(BizEventListener.class);
        return annotation != null ?
            handleEventProcessing(joinPoint, annotation, event) :
            joinPoint.proceed();
    }

    /**
     * 处理事件
     */
    private Object handleEventProcessing(ProceedingJoinPoint joinPoint, BizEventListener annotation, BizEvent<?> event) throws Throwable {
        BizEventTypeEnum eventType = event.getEventType();
        boolean eventAsync = event.isAsync();
        BizEventTypeEnum[] listenedEventTypes = BizEventTypeEnum.values();
        if (annotation.listenedBizEvent().length > 0) {
            listenedEventTypes = annotation.listenedBizEvent();
        }
        BizEventInfo eventInfo = event.getEventInfo();
        if ((new HashSet<>(Arrays.asList(listenedEventTypes))).contains(eventType)) {
            //事件命中监听的业务事件类型才进行调用，否则直接返回
            eventInfo.setStatus(BizEventStatusEnum.DOING.getCode());
            eventInfo.setBeginProcessTime(new Date());
            //默认使用异步方法，如果注解上或者事件上标记了不使用异步则进行同步调用
            boolean async = annotation.async() && eventAsync;
            eventInfo.setAsync(async);
            bizEventService.updateEventInfo(eventInfo);
            if (async) {
                //类内调用异步方法必须经过代理类，否则异步注解不生效
                SpringUtil.getBean(BizEventListenerAspect.class).callListenFunctionAsync(joinPoint, event);
            } else {
                return callListenFunction(joinPoint, event);
            }
        } else {
            log.warn("doEventProcess none match:{}", eventInfo.getId());
            eventInfo.setStatus(BizEventStatusEnum.NONE_MATCH.getCode());
            bizEventService.updateEventInfo(eventInfo);
        }
        return null;
    }
}
